package com.heima.article.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.BehaviorConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;

import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.thread.AppThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service//表示这是一个业务逻辑层
@Transactional//事务注解，表示该方法是一个事务方法，在调用该方法时，如果出现异常，则会进行回滚
@Slf4j//日志注解，表示该类使用了日志功能，在该类中所有方法调用前后，会打印日志信息
public class ApArticleServiceImpl  extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

	// 单页最大加载的数字，定义为常量，便于修改，避免硬编码
	private final static short MAX_PAGE_SIZE = 50;

	@Autowired
	private ApArticleMapper apArticleMapper;

	/**
	 * 根据参数加载文章列表（根据参数类型加载不同类型的文章）
	 * @param loadtype 1为加载更多  2为加载最新
	 * @param dto
	 * @return
	 */
	@Override
	public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
		//1.校验参数，提高代码的健壮性
		Integer size = dto.getSize();
		//如果size为null或0，则默认设置为10，即每页展示10条数据
		if(size == null || size == 0){
			size = 10;
		}
		//分页的值不超过50，如果是固定的值，我们通常会将它抽取成为常量(MAX_PAGE_SIZE),让代码更加优雅
		//调用math类种的min，min方法的作用是选取size和MAX_PAGE_SIZE中的最小值
		size = Math.min(size,MAX_PAGE_SIZE);
		//将当前页展示的数据条数赋值给dto,即修改dto中的size值
		dto.setSize(size);

		//类型参数检验，检查loadtype参数是否有效，即loadtype不为1也不为2的话，那不符合要求
		if(!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)&&!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
			//如果loadtype不在有效范围内，则默认设置为ArticleConstants.LOADTYPE_LOAD_MORE，即加载更多文章
			//即要是前端不指定或者乱指定的话，就默认加载更多
			loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
		}

		//文章频道校验，如果dto中的tag字段为空或未定义，则设置默认的tag（频道）
		if(StringUtils.isEmpty(dto.getTag())){
			//将前端传递的标签指定为默认标签
			dto.setTag(ArticleConstants.DEFAULT_TAG);
		}

		//时间校验，如果dto中的时间参数为空，则设置为当前时间
		if(dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
		if(dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());

		//2.查询数据，根据loadtype参数，查询数据库中对应的数据
		List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);

		//3.结果封装
		return ResponseResult.okResult(apArticles);
	}


	@Autowired
	private ApArticleConfigMapper apArticleConfigMapper;

	@Autowired
	private ApArticleContentMapper apArticleContentMapper;

	@Autowired
	private ArticleFreemarkerService articleFreemarkerService;

	/**
	 * 保存app端相关文章(根据id来判断是保存还是更新文章操作)
	 * @param dto
	 * @return
	 */
	@Override
	public ResponseResult saveArticle(ArticleDto dto) {
		//1.检查参数,一般都需要检查参数的合法性，提高代码的健壮性
		if(dto == null){
			//如果dto为空，则返回参数错误的结果
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}

		//2.创建文章对象，将dto中的数据复制到apArticle对象中
		ApArticle apArticle = new ApArticle();
		BeanUtils.copyProperties(dto,apArticle);

		//2.判断是否存在id
		if(dto.getId() == null){
			//2.1 如果不存在id，说明是新增操作，则保存文章，文章配置和文章内容
			//保存文章（涉及多个表的删除/修改操作，需要使用事务注解，在类上已开启事务）
			//default boolean save(T entity)这个方法是mybatis-plus提供的，可以直接保存对象，
			//但是需要注意的是，如果对象中有主键，则会更新主键对应的记录，如果没有主键，则会插入一条新的记录
			save(apArticle);

			//保存 文章配置
			//在这里我们根据文章的id来创建文章配置对象，其他配置信息默认即可
			ApArticleConfig apArticleConfig = new ApArticleConfig(apArticle.getId());
			//给文章配置数据库中插入数据
			apArticleConfigMapper.insert(apArticleConfig);

			//保存 文章内容
			//创建一个文章内容对象，并设置文章id和文章内容
			ApArticleContent apArticleContent = new ApArticleContent();
			apArticleContent.setArticleId(apArticle.getId());
			apArticleContent.setContent(dto.getContent());
			//给文章内容数据库中插入数据
			apArticleContentMapper.insert(apArticleContent);
		}else {
			//2.2 存在id   修改  文章  文章内容
			//修改  文章
			updateById(apArticle);

			//修改文章内容
			ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getId()));
			apArticleContent.setContent(dto.getContent());
			//更新文章内容
			apArticleContentMapper.updateById(apArticleContent);
		}

		//异步调用，生成静态文件上传到minio中
		//因为生成静态文件比较耗时，并且时效性要求不高，所以我们异步调用，可以提升响应速度
		articleFreemarkerService.buildArticleToMinIO(apArticle,dto.getContent());

		//3.结果返回  文章的id
		return ResponseResult.okResult(apArticle.getId());
	}

	@Autowired
	private CacheService cacheService;

	/**
	 * 查看文章，数据回显
	 * @param dto
	 * @return
	 */
	@Override
	public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {

		//检查参数
		if (dto == null || dto.getArticleId() == null || dto.getAuthorId() == null) {
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}

		boolean isfollow = false, islike = false, isunlike = false, iscollection = false;

		ApUser user = AppThreadLocalUtil.getUser();
		if (user != null) {
			//喜欢行为
			String likeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.LIKE_BEHAVIOR + dto.getAuthorId().toString(), user.getId().toString());
			if (StringUtils.isNotBlank(likeBehaviorJson)) {
				islike = true;
			}
			//不喜欢的行为
			String unLikeBehaviorJson = (String) cacheService.hGet(BehaviorConstants.UN_LIKE_BEHAVIOR + dto.getAuthorId().toString(), user.getId().toString());
			if (StringUtils.isNotBlank(unLikeBehaviorJson)) {
				isunlike = true;
			}
			//是否收藏
			String collctionJson = (String) cacheService.hGet(BehaviorConstants.COLLECTION_BEHAVIOR+user.getId(),dto.getArticleId().toString());
			if(StringUtils.isNotBlank(collctionJson)){
				iscollection = true;
			}
			//是否关注
			Double score = cacheService.zScore(BehaviorConstants.APUSER_FOLLOW_RELATION + user.getId(), dto.getAuthorId().toString());
			System.out.println(score);
			if(score != null){
				isfollow = true;
			}
		}

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("isfollow", isfollow);
		resultMap.put("islike", islike);
		resultMap.put("isunlike", isunlike);
		resultMap.put("iscollection", iscollection);

		return ResponseResult.okResult(resultMap);
	}
}